home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / LinAlg 3.1 / LinAlg / matrix_inv.cc < prev    next >
Encoding:
Text File  |  1995-12-20  |  4.7 KB  |  155 lines  |  [TEXT/ALFA]

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *            Linear Algebra Package
  6.  *
  7.  *            Find the matrix inverse
  8.  *        for matrices of general and special forms
  9.  *
  10.  * $Id: matrix_inv.cc,v 3.1 1995/01/31 17:17:41 oleg Exp oleg $
  11.  *
  12.  ************************************************************************
  13.  */
  14.  
  15. #include "LinAlg.h"
  16. #include <math.h>
  17. #include <alloca.h>
  18.  
  19. /*
  20.  *------------------------------------------------------------------------
  21.  *
  22.  *        The most general (Gauss-Jordan) matrix inverse
  23.  *
  24.  * This method works for any matrix (which of course must be square and
  25.  * non-singular). Use this method only if none of specialized algorithms
  26.  * (for symmetric, tridiagonal, etc) matrices isn't applicable/available.
  27.  * Also, the matrix to invert has to be _well_ conditioned:
  28.  * Gauss-Jordan eliminations (even with pivoting) perform poorly for
  29.  * near-singular matrices (e.g., Hilbert matrices).
  30.  *
  31.  * The method inverts matrix inplace and returns the determinant if
  32.  * determ_ptr was specified as not nil. determinant will be exactly zero
  33.  * if the matrix turns out to be (numerically) singular. If determ_ptr is
  34.  * nil and matrix happens to be singular, throw up.
  35.  *
  36.  * The algorithm perform inplace Gauss-Jordan eliminations with
  37.  * full pivoting. It was adapted from my Algol-68 "translation" (ca 1986)
  38.  * of the FORTRAN code described in
  39.  * Johnson, K. Jeffrey, "Numerical methods in chemistry", New York,
  40.  * N.Y.: Dekker, c1980, 503 pp, p.221
  41.  *
  42.  * Note, since it's much more efficient to perform operations on matrix
  43.  * columns rather than matrix rows (due to the layout of elements in the
  44.  * matrix), the present method implements a "transposed" algorithm.
  45.  *
  46.  */
  47.  
  48. Matrix& Matrix::invert(double * determ_ptr)
  49. {
  50.   is_valid();
  51.   if( nrows != ncols )
  52.     info(),
  53.     _error("Matrix to invert must be square");
  54.  
  55.   double determinant = 1;
  56.   const double singularity_tolerance = 1e-35;
  57.  
  58.                 // Locations of pivots (indices start with 0)
  59.   struct Pivot { int row, col; } * const pivots = 
  60.               (Pivot*)alloca(ncols*sizeof(Pivot));
  61.   bool * const was_pivoted = (bool*)alloca(nrows*sizeof(bool)); 
  62.   memset(was_pivoted,false,nrows*sizeof(bool));
  63.   register Pivot * pivotp;
  64.  
  65.   for(pivotp = &pivots[0]; pivotp < &pivots[ncols]; pivotp++)
  66.   {
  67.     int prow = 0, pcol = 0;        // Location of a pivot to be
  68.     {                    // Look through all non-pivoted cols
  69.       REAL max_value = 0;        // (and rows) for a pivot (max elem)
  70.       for(register int j=0; j<ncols; j++)
  71.     if( !was_pivoted[j] )
  72.     {
  73.       register REAL * cp;
  74.       register int k;
  75.       REAL curr_value = 0;
  76.       for(k=0,cp=index[j]; k<nrows; k++,cp++)
  77.         if( !was_pivoted[k] && (curr_value = fabs(*cp)) > max_value )
  78.           max_value = curr_value, prow = k, pcol = j;
  79.     }
  80.       if( max_value < singularity_tolerance )
  81.     if( determ_ptr )
  82.     {
  83.       *determ_ptr = 0;
  84.       return *this;
  85.     }
  86.         else
  87.       _error("Matrix turns out to be singular: can't invert");
  88.       pivotp->row = prow;
  89.       pivotp->col = pcol;
  90.     }
  91.  
  92.     if( prow != pcol )            // Swap prow-th and pcol-th columns to
  93.     {                    // bring the pivot to the diagonal
  94.       register REAL * cr = index[prow];
  95.       register REAL * cc = index[pcol];
  96.       for(register int k=0; k<nrows; k++)
  97.       {
  98.     REAL temp = *cr; *cr++ = *cc; *cc++ = temp;
  99.       }
  100.     }
  101.     was_pivoted[prow] = true;
  102.  
  103.     {                    // Normalize the pivot column and
  104.       register REAL * pivot_cp = index[prow];
  105.       double pivot_val = pivot_cp[prow];    // pivot is at the diagonal
  106.       determinant *= pivot_val;        // correct the determinant
  107.       pivot_cp[prow] = true;
  108.       for(register int k=0; k<nrows; k++)
  109.     *pivot_cp++ /= pivot_val;
  110.     }
  111.  
  112.     {                    // Perform eliminations
  113.       register REAL * pivot_rp = elements + prow;    // pivot row
  114.       for(register int k=0; k<nrows; k++, pivot_rp += nrows)
  115.     if( k != prow )
  116.     {
  117.       double temp = *pivot_rp;
  118.       *pivot_rp = 0;
  119.       register REAL * pivot_cp = index[prow];    // pivot column
  120.       register REAL * elim_cp  = index[k];        // elimination column
  121.       for(register int l=0; l<nrows; l++)
  122.         *elim_cp++ -= temp * *pivot_cp++;
  123.     }
  124.     }
  125.   }
  126.  
  127.   int no_swaps = 0;        // Swap exchanged *rows* back in place
  128.   for(pivotp = &pivots[ncols-1]; pivotp >= &pivots[0]; pivotp--)
  129.     if( pivotp->row != pivotp->col )
  130.     {
  131.       no_swaps++;
  132.       register REAL * rp = elements + pivotp->row;
  133.       register REAL * cp = elements + pivotp->col;
  134.       for(register int k=0; k<ncols; k++, rp += nrows, cp += nrows)
  135.       {
  136.     REAL temp = *rp; *rp = *cp; *cp = temp;
  137.       }
  138.     }
  139.  
  140.   if( determ_ptr )
  141.     *determ_ptr = ( no_swaps & 1 ? -determinant : determinant );
  142.  
  143.   return *this;
  144. }
  145.  
  146.  
  147.                 // Allocate new matrix and set it to inv(m)
  148. void Matrix::_invert(const Matrix& m)
  149. {
  150.   m.is_valid();
  151.   allocate(m.nrows,m.ncols,m.row_lwb,m.col_lwb);
  152.   *this = m;
  153.   invert(0);
  154. }
  155.